package com.atguigu.c_designed.m2stucturalModel.s6FlyWeight;

import java.util.HashMap;

/**
 * 说到享元模式，第一个想到的应该就是池技术了，String常量池、数据库连接池、缓冲池等等都是享元模式的应用，所以说享元模式是池技术的重要实现方式。
 *
 *  比如我们每次创建字符串对象时，都需要创建一个新的字符串对象的话，内存开销会很大，所以如果第一次创建了字符串对象“adam“，
 * 下次再创建相同的字符串”adam“时，只是把它的引用指向”adam“，这样就实现了”adam“字符串再内存中的共享。
 *
 * 举个最简单的例子，网络联机下棋的时候，一台服务器连接了多个客户端（玩家），如果我们每个棋子都要创建对象，
 * 那一盘棋可能就有上百个对象产生，玩家多点的话，因为内存空间有限，一台服务器就难以支持了，
 * 所以这里要使用享元模式，将棋子对象减少到几个实例。下面给出享元模式的定义。
 * 享元模式（Flyweight），运用共享技术有效地支持大量细粒度的对象。
 */
public class FlyWeightClientDemo {

    public static void main(String[] args) {
        WebSiteFactory factory = new WebSiteFactory();
        User user = new User("张三");
        WebSite fx = factory.getWebSiteCategory("产品展示");
        fx.use(user);
        User user1 = new User("张1");

        WebSite fy = factory.getWebSiteCategory("产品展示");
        fy.use(user1);
        User user2 = new User("张2");

        WebSite fz = factory.getWebSiteCategory("产品展示");
        fz.use(user2);
        User user3 = new User("张3");

        WebSite fa = factory.getWebSiteCategory("博客");
        fa.use(user3);
        User user4 = new User("张4");

        WebSite fb = factory.getWebSiteCategory("博客");
        fb.use(user4);
        User user5 = new User("张5");

        WebSite fc = factory.getWebSiteCategory("博客");
        fc.use(user5);
        System.out.println("网站分类总数为：" + factory.getWebSiteCount());
    }


}

/**
 * 1. 何时使用
 * 系统中有大量对象时
 * 这些对象消耗大量内存时
 * 这些对象的状态大部分可以外部化时
 * 　　2. 方法
 * 用唯一标识码判断，如果在内存中有，则返回这个唯一标识码所标识的对象，用HashMap/HashTable存储
 * 　　3. 优点
 * 大大减少了对象的创建，降低了程序内存的占用，提高效率
 * 　　4. 缺点
 * 提高了系统的复杂度。需要分离出内部状态和外部状态，而外部状态具有固化特性，不应该随着内部状态的改变而改变
 *  　　5. 使用场景
 * 系统中存在大量相似对象
 * 需要缓冲池的场景
 *  　　6. 应用实例
 * String常量池
 * 数据库连接池
 *  　　7. 注意事项
 * 注意划分内部状态和外部状态，否则可能会引起线程安全问题
 * 这些类必须有一个工厂类加以控制
 */
abstract class WebSite {

    public abstract void use(User user);
}

class ConcreteWebSite extends WebSite {
    private String type;

    public ConcreteWebSite(String type) {
        this.type = type;
    }

    @Override
    public void use(User user) {
        System.out.println("网站分类：" + type+"使用者："+user.getName());
    }
}

class WebSiteFactory {
    HashMap<String, WebSite> map = new HashMap<>();

    public WebSite getWebSiteCategory(String key){
        WebSite webSite = map.get(key);
        if(webSite == null){
            map.put(key, new ConcreteWebSite(key));
        }
        return map.get(key);
    }

    public int getWebSiteCount(){
        return map.size();
    }
}

class User {
    private String name;

    public User(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}